<?php

/**
 * @file WCTagsRelator.php
 * @author Alejandro Dario Simi
 * @date $Date: 2014-06-03 03:40:32 +0000 (Tue, 03 Jun 2014) $
 *
 * $Id: WCTagsRelator.php 112 2014-06-03 03:40:32Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/trunk/includes/rules/WCTagsRelator.php $
 */

/**
 * @class WCTagsRelator
 * @abstract
 * @author Alejandro Dario Simi
 *
 */
class WCTagsRelator extends WCRuleAction {
	//
	// Constants.
	public static $TYPE = "TARE";
	public static $ModeALSO = "ALSO";
	public static $ModeIF = "IF";
	public static $ModeMERGE = "MERGE";
	public static $ModePOOL = "POOL";
	//
	// Protected variables.
	protected $_also = array();
	protected $_seekerComicStmt = false;
	protected $_seekerImageStmt = false;
	protected $_tags = array();
	//
	// Public methods.
	public function generatePreviews() {
		$out = false;

		$previewsManager = WCPreviewsManager::I();

		$previews = array();
		if(in_array($this->mode, array(self::$ModeALSO, self::$ModeMERGE, self::$ModePOOL))) {
			foreach($this->_tags as $tag) {
				$previews = array_merge($previews, $previewsManager->getPreviews($this->group(), WCPreviewsManager::TypeTag, $tag->id()));
			}
		}
		if(in_array($this->mode, array(self::$ModeIF))) {
			foreach($this->_also as $tag) {
				$previews = array_merge($previews, $previewsManager->getPreviews($this->group(), WCPreviewsManager::TypeTag, $tag->id()));
			}
		}
		$previews = array_unique($previews);

		$position = 0;
		foreach($previews as $preview) {
			if($previewsManager->addPreviews($this->group(), WCPreviewsManager::TypeRuleAction, $this->id(), $position, $preview)) {
				$position++;
			}

			if($position >= WCPreviewsManager::MaxPerRuleAction) {
				break;
			}
		}

		if(count($previews) > 0) {
			$this->_previewsLoaded = false;
			$out = true;
		}
		return $out;
	}
	public function mode() {
		return $this->mode;
	}
	public function reload() {
		$this->_tags = array();
		$this->_also = array();

		parent::reload();
	}
	public function run() {
		$out = array();

		$stmtId = __CLASS__."::".__FUNCTION__."[seeker-cmc]";
		if($this->_db->has($stmtId)) {
			$this->_seekerComicStmt = $this->_db->get($stmtId);
		} else {
			$query = "select  mem_comic as id\n";
			$query .= "from    {$this->_dbprefix}tag_comics\n";
			$query .= "where   mem_group = :group\n";
			$query .= " and    mem_tag   = :tag_search\n";
			$query .= " and    mem_comic not in (\n";
			$query .= "                select  mem_comic\n";
			$query .= "                from    {$this->_dbprefix}tag_comics\n";
			$query .= "                where   mem_group = :group\n";
			$query .= "                 and    mem_tag   = :tag_toadd\n";
			$query .= "        )\n";
			$this->_seekerComicStmt = $this->_db->prepare($stmtId, $query);
		}
		$stmtId = __CLASS__."::".__FUNCTION__."[seeker-img]";
		if($this->_db->has($stmtId)) {
			$this->_seekerImageStmt = $this->_db->get($stmtId);
		} else {
			$query = "select  mem_image as id\n";
			$query .= "from    {$this->_dbprefix}tag_images\n";
			$query .= "where   mem_group = :group\n";
			$query .= " and    mem_tag   = :tag_search\n";
			$query .= " and    mem_image not in (\n";
			$query .= "                select  mem_image\n";
			$query .= "                from    {$this->_dbprefix}tag_images\n";
			$query .= "                where   mem_group = :group\n";
			$query .= "                 and    mem_tag   = :tag_toadd\n";
			$query .= "        )\n";
			$this->_seekerImageStmt = $this->_db->prepare($stmtId, $query);
		}

		switch($this->mode) {
			case self::$ModeALSO:
				$out = $this->runModeALSOComic();
				$out = $this->runModeALSOImage();
				break;
			case self::$ModeIF:
				$out = $this->runModeIFComic();
				$out = $this->runModeIFImage();
				break;
			case self::$ModeMERGE:
				$out = $this->runModeMERGEComic();
				$out = $this->runModeMERGEImage();
				break;
			case self::$ModePOOL:
				$out = $this->runModePOOLComic();
				$out = $this->runModePOOLImage();
				break;
		}

		return $out;
	}
	public function tags($sorted = false) {
		if($sorted) {
			uasort($this->_tags, "WCTag::NameSort");
		}
		return $this->_tags;
	}
	public function tagsAlso($sorted = false) {
		if($sorted) {
			uasort($this->_also, "WCTag::NameSort");
		}
		return $this->_also;
	}
	public function tagFrom() {
		return $this->_tagFrom;
	}
	public function tagFromName() {
		return $this->_tagFromName;
	}
	public function tagTo() {
		return $this->_tagTo;
	}
	public function tagToName() {
		return $this->_tagToName;
	}
	public function words() {
		$out = array();

		if($this->target) {
			$out = array_merge($out, WCSearch::StringToPatterns($this->target));
		}
		foreach($this->_tags as $tag) {
			$out = array_merge($out, WCSearch::StringToPatterns($tag->name));
		}
		foreach($this->_also as $tag) {
			$out = array_merge($out, WCSearch::StringToPatterns($tag->name));
		}

		return $out;
	}
	//
	// Protected methods.
	protected function checkTarget() {
		if($this->target) {
			$tag = WCTagsHolder::I()->itemByNameInGroup($this->target, $this->_group);
			$tag = array_shift($tag);
			if(!$tag) {
				$tag = WCTagsHolder::I()->createInGroup($this->target, $this->_group);
			}
		}
	}
	protected function enforceMode() {
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "update  {$this->_dbprefix}acts_tare\n";
			$query .= "set     act_mode = :mode\n";
			$query .= "where   act_rule   = :rule\n";
			$query .= " and    act_group  = :group\n";
			$query .= " and    act_action = :id\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":rule" => $this->_ruleId,
			":group" => $this->_group->id,
			":id" => $this->_id,
			":mode" => $this->mode
		);
		$stmt->execute($params);
	}
	protected function load() {
		if($this->_type != self::$TYPE) {
			trigger_error("Loaded type and class type are different.", E_USER_ERROR);
		}

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[1]";
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query .= "from    {$this->_dbprefix}acts_tare\n";
			$query .= "where   act_rule   = :rule\n";
			$query .= " and    act_group  = :group\n";
			$query .= " and    act_action = :id\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":rule" => $this->_ruleId,
			":group" => $this->_group->id,
			":id" => $this->_id
		);

		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			foreach($stmt->fetchAll() as $row) {
				if($row["act_tag"] && !isset($this->_tags[$row["act_tag"]])) {
					$tag = WCTagsHolder::I()->item($row["act_tag"]);
					if($tag && $tag->ok()) {
						$this->_tags[$tag->id] = $tag;
					}
				}

				if($row["act_also"] && !isset($this->_also[$row["act_also"]])) {
					$tag = WCTagsHolder::I()->item($row["act_also"]);
					if($tag && $tag->ok()) {
						$this->_also[$tag->id] = $tag;
					}
				}
			}

			if($row["act_mode"] != $this->mode) {
				$this->enforceMode();
			}
		}

		$this->_ok = true;

		if(!$this->mode) {
			$this->mode = self::$ModeALSO;
		}
		if(count($this->_tags) && ($this->mode == self::$ModeALSO || $this->mode == self::$ModeIF)) {
			$tag = array_values($this->_tags);
			$tag = array_shift($tag);
			$this->target = $tag->name;
		}
	}
	protected function removeSpecific() {
		$out = true;

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from {$this->_dbprefix}acts_tare\n";
			$query .= "where       act_rule   = :rule\n";
			$query .= " and        act_group  = :group\n";
			$query .= " and        act_action = :id\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":rule" => $this->_ruleId,
			":group" => $this->_group->id,
			":id" => $this->_id
		);

		if(!$stmt->execute($params)) {
			$out = false;
		}

		return $out;
	}
	protected function runModeALSOComic() {
		$out = array();

		if($this->_ok) {
			$tagsCount = count($this->_tags);
			if($tagsCount > 1) {
				$this->setConflict(true);
				trigger_error(__CLASS__."::".__FUNCTION__."(): This action seems to be corrupt.", E_USER_ERROR);
			} elseif($tagsCount == 1) {
				$tag = WCTagsHolder::I()->itemByNameInGroup($this->target, $this->_group);
				$tag = array_shift($tag);

				if($tag && $tag->ok()) {
					foreach($this->_also as $toAdd) {
						//
						// Searching all tags.
						$comicIds = $this->seekerComic($tag, $toAdd);
						foreach($comicIds as $id) {
							$comic = WCComicsHolder::I()->item($id);
							if($comic && $comic->ok() && $comic->group == $this->_group->id) {
								if(!isset($out["items"])) {
									$out["items"] = array();
								}
								//
								// It seems not
								// to be present,
								// therefore it
								// is added.
								$aux = array(
									"action" => "Adding '{$comic->name}' ({$comic->id}[{$comic->directory}]) to tag '{$toAdd->name}' ({$toAdd->id})",
									"member" => $comic,
									"status" => true,
									"fatal" => true
								);
								$toAdd->addComic($comic);
								$out["items"][] = $aux;
							}
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModeALSOImage() {
		$out = array();

		if($this->_ok) {
			$tagsCount = count($this->_tags);
			if($tagsCount > 1) {
				$this->setConflict(true);
				trigger_error(__CLASS__."::".__FUNCTION__."(): This action seems to be corrupt.", E_USER_ERROR);
			} elseif($tagsCount == 1) {
				$tag = WCTagsHolder::I()->itemByNameInGroup($this->target, $this->_group);
				$tag = array_shift($tag);

				if($tag && $tag->ok()) {
					foreach($this->_also as $toAdd) {
						//
						// Searching all tags.
						$imageIds = $this->seekerImage($tag, $toAdd);
						foreach($imageIds as $id) {
							$image = WCImagesHolder::I()->item($id);
							if($image && $image->ok() && $image->group == $this->_group->id) {
								if(!isset($out["items"])) {
									$out["items"] = array();
								}
								//
								// It seems not
								// to be present,
								// therefore it
								// is added.
								$aux = array(
									"action" => "Adding '{$image->name}' ({$image->id}[{$image->directory}]) to tag '{$toAdd->name}' ({$toAdd->id})",
									"member" => $image,
									"status" => true,
									"fatal" => true
								);
								$toAdd->addImage($image);
								$out["items"][] = $aux;
							}
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModeIFComic() {
		$out = array();

		if($this->_ok) {
			$tagsCount = count($this->_tags);
			if($tagsCount > 1) {
				$this->setConflict(true);
				trigger_error(__CLASS__."::".__FUNCTION__."(): This action seems to be corrupt.", E_USER_ERROR);
			} elseif($tagsCount == 1) {
				$tag = WCTagsHolder::I()->itemByNameInGroup($this->target, $this->_group);
				$tag = array_shift($tag);

				if($tag && $tag->ok()) {
					$comicIds = array();
					foreach($this->_also as $toSearch) {
						//
						// Searching all tags.
						$comicIds = array_merge($comicIds, $this->seekerComic($toSearch, $tag));
					}
					$comicIds = array_unique($comicIds);

					foreach($comicIds as $id) {
						$comic = WCComicsHolder::I()->item($id);
						if($comic && $comic->ok() && $comic->group == $this->_group->id) {
							if(!isset($out["items"])) {
								$out["items"] = array();
							}
							//
							// It seems not to be
							// present, therefore
							// it's added.
							$aux = array(
								"action" => "Adding '{$comic->name}' ({$comic->id}[{$comic->directory}]) to tag '{$tag->name}' ({$tag->id})",
								"member" => $comic,
								"status" => true,
								"fatal" => true
							);
							$tag->addComic($comic);
							$out["items"][] = $aux;
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModeIFImage() {
		$out = array();

		if($this->_ok) {
			$tagsCount = count($this->_tags);
			if($tagsCount > 1) {
				$this->setConflict(true);
				trigger_error(__CLASS__."::".__FUNCTION__."(): This action seems to be corrupt.", E_USER_ERROR);
			} elseif($tagsCount == 1) {
				$tag = WCTagsHolder::I()->itemByNameInGroup($this->target, $this->_group);
				$tag = array_shift($tag);

				if($tag && $tag->ok()) {
					$imageIds = array();
					foreach($this->_also as $toSearch) {
						//
						// Searching all tags.
						$imageIds = array_merge($imageIds, $this->seekerImage($toSearch, $tag));
					}
					$imageIds = array_unique($imageIds);

					foreach($imageIds as $id) {
						$image = WCImagesHolder::I()->item($id);
						if($image && $image->ok() && $image->group == $this->_group->id) {
							if(!isset($out["items"])) {
								$out["items"] = array();
							}
							//
							// It seems not to be
							// present, therefore
							// it's added.
							$aux = array(
								"action" => "Adding '{$image->name}' ({$image->id}[{$image->directory}]) to tag '{$tag->name}' ({$tag->id})",
								"member" => $image,
								"status" => true,
								"fatal" => true
							);
							$tag->addImage($image);
							$out["items"][] = $aux;
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModeMERGEComic() {
		$out = array();

		if($this->_ok) {
			$tagsCount = count($this->_tags);
			if($tagsCount > 1) {
				$this->setConflict(true);
				trigger_error(__CLASS__."::".__FUNCTION__."(): This action seems to be corrupt.", E_USER_ERROR);
			} elseif($tagsCount == 1) {
				$tag = WCTagsHolder::I()->itemByNameInGroup($this->target, $this->_group);
				$tag = array_shift($tag);

				if($tag && $tag->ok()) {
					$wrongTags = array();
					foreach($this->_also as $toSearch) {
						if($toSearch && $toSearch->ok() && $toSearch->hasComics()) {
							$wrongTags[] = $toSearch;
						}
					}

					foreach($wrongTags as $wrongTag) {
						foreach($wrongTag->comicsFull() as $comic) {
							if($comic && $comic->ok() && $comic->group == $this->_group->id) {
								if(!isset($out["items"])) {
									$out["items"] = array();
								}

								$aux = array(
									"action" => "Adding comic {$comic} to tag {$tag}",
									"member" => $comic,
									"status" => true,
									"fatal" => true
								);
								$tag->addComic($comic);
								$out["items"][] = $aux;

								$aux = array(
									"action" => "Removing comic {$comic} from tag {$wrongTag}",
									"member" => $comic,
									"status" => true,
									"fatal" => true
								);
								$wrongTag->removeComic($comic);
								$out["items"][] = $aux;
							}
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModeMERGEImage() {
		$out = array();

		if($this->_ok) {
			$tagsCount = count($this->_tags);
			if($tagsCount > 1) {
				$this->setConflict(true);
				trigger_error(__CLASS__."::".__FUNCTION__."(): This action seems to be corrupt.", E_USER_ERROR);
			} elseif($tagsCount == 1) {
				$tag = WCTagsHolder::I()->itemByNameInGroup($this->target, $this->_group);
				$tag = array_shift($tag);

				if($tag && $tag->ok()) {
					$wrongTags = array();
					foreach($this->_also as $toSearch) {
						if($toSearch && $toSearch->ok() && $toSearch->hasImages()) {
							$wrongTags[] = $toSearch;
						}
					}

					foreach($wrongTags as $wrongTag) {
						foreach($wrongTag->imagesFull() as $image) {
							if($image && $image->ok() && $image->group == $this->_group->id) {
								if(!isset($out["items"])) {
									$out["items"] = array();
								}

								$aux = array(
									"action" => "Adding image {$image} to tag {$tag}",
									"member" => $image,
									"status" => true,
									"fatal" => true
								);
								$tag->addImage($image);
								$out["items"][] = $aux;

								$aux = array(
									"action" => "Removing image {$image} from tag {$wrongTag}",
									"member" => $image,
									"status" => true,
									"fatal" => true
								);
								$wrongTag->removeImage($image);
								$out["items"][] = $aux;
							}
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModePOOLComic() {
		$out = array();

		if($this->_ok) {
			$idMembers = array();
			//
			// Check every loaded tag.
			foreach($this->_tags as $id => $tag) {
				//
				// Select every comic of every tag to be added to
				// the rest of the tags.
				foreach($tag->comicsFull() as $comic) {
					//
					// Checking all the rest of tags and if
					// they don't have the comic, adding it.
					foreach($this->_tags as $subId => $subTag) {
						if($id == $subId) {
							// If it's checking over
							// the same tag, it skips
							// it.
							continue;
						}
						//
						// Caching comics ids to gain
						// performance.
						if(!isset($idMembers[$subTag->id])) {
							$idMembers[$subTag->id] = $subTag->comics();
						}
						//
						// Checking if the selected comic
						// is in the other tag.
						if(!in_array($comic->id, $idMembers[$subTag->id])) {
							//
							// It seems not to be
							// present, therefore it
							// is added.
							$aux = array(
								"action" => "Adding '{$comic->name}' ({$comic->id}[{$comic->directory}]) to tag '{$subTag->name}' ({$subTag->id})",
								"member" => $comic,
								"status" => true,
								"fatal" => true
							);
							$subTag->addComic($comic);
							$out["items"][] = $aux;

							unset($idMembers[$subTag->id]);
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModePOOLImage() {
		$out = array();

		if($this->_ok) {
			$idMembers = array();
			//
			// Check every loaded tag.
			foreach($this->_tags as $id => $tag) {
				//
				// Select every image of every tag to be added to
				// the rest of the tags.
				foreach($tag->imagesFull() as $image) {
					//
					// Checking all the rest of tags and if
					// they don't have the image, adding it.
					foreach($this->_tags as $subId => $subTag) {
						if($id == $subId) {
							// If it's checking over
							// the same tag, it skips
							// it.
							continue;
						}
						//
						// Caching images ids to gain
						// performance.
						if(!isset($idMembers[$subTag->id])) {
							$idMembers[$subTag->id] = $subTag->images();
						}
						//
						// Checking if the selected image
						// is in the other tag.
						if(!in_array($image->id, $idMembers[$subTag->id])) {
							//
							// It seems not to be
							// present, therefore it
							// is added.
							$aux = array(
								"action" => "Adding '{$image->name}' ($image->id[$image->directory]) to tag '{$subTag->name}' ({$subTag->id})",
								"member" => $image,
								"status" => true,
								"fatal" => true
							);
							$subTag->addImage($image);
							$out["items"][] = $aux;

							unset($idMembers[$subTag->id]);
						}
					}
				}
			}
		}

		return $out;
	}
	protected function seekerComic(WCTag &$search, WCTag &$toadd) {
		$out = array();

		if($search->group == $toadd->group) {
			$params = array(
				":group" => $search->group,
				":tag_search" => $search->id,
				":tag_toadd" => $toadd->id
			);
			if($this->_seekerComicStmt->execute($params)) {
				foreach($this->_seekerComicStmt->fetchAll() as $row) {
					$out[] = $row["id"];
				}
			}
		}

		return $out;
	}
	protected function seekerImage(WCTag &$search, WCTag &$toadd) {
		$out = array();

		if($search->group == $toadd->group) {
			$params = array(
				":group" => $search->group,
				":tag_search" => $search->id,
				":tag_toadd" => $toadd->id
			);
			if($this->_seekerImageStmt->execute($params)) {
				foreach($this->_seekerImageStmt->fetchAll() as $row) {
					$out[] = $row["id"];
				}
			}
		}

		return $out;
	}
	//
	// Class Methods
	public static function LockedTagIds(WCGroup &$group) {
		$out = array();

		$db = WCConnection::Instance();
		$dbprefix = $db->prefix();

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[1]";
		if($db->has($stmtId)) {
			$stmt = $db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query .= "from    {$dbprefix}acts_tare\n";

			$stmt = $db->prepare($stmtId, $query);
		}

		if($stmt->execute()) {
			foreach($stmt->fetchAll() as $row) {
				$out[] = $row["act_tag"];
				if($row["act_also"]) {
					$out[] = $row["act_also"];
				}
			}
		}

		return array_unique($out);
	}
	public static function MarkConflicts() {
		$out = 0;

		$db = WCConnection::Instance();
		$dbprefix = $db->prefix();
		//
		// Looking for tags that move contents to two different
		// directories.
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[POOL-MULTI_TAG]";
		if($db->has($stmtId)) {
			$stmt = $db->get($stmtId);
		} else {
			$query = "update  {$dbprefix}rule_actions\n";
			$query .= "set     rel_ra_conflict = 'Y'\n";
			$query .= "where   (rel_ra_rule,rel_ra_action) in (\n";
			$query .= "                select  act_rule,\n";
			$query .= "                        act_action\n";
			$query .= "                from    {$dbprefix}acts_dor\n";
			$query .= "                where   act_tag in (\n";
			$query .= "                                select  act_tag\n";
			$query .= "                                from    {$dbprefix}acts_dor\n";
			$query .= "                                where   act_mode = 'POOL'\n";
			$query .= "                                group by act_tag\n";
			$query .= "                                having count(distinct(act_action)) > 1))\n";

			$stmt = $db->prepare($stmtId, $query);
		}

		if($stmt->execute()) {
			$out += $stmt->rowCount();
		}

		return $out;
	}
}
